/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import dataRdb from '@ohos.data.relationalStore'
import { DaoConfig } from './db/DaoConfig'
import { SqlInfoBuilder } from './db/sqlite/SqlInfoBuilder'
import { DaoType } from './db/DaoType'
import { QueryCallBack } from './db/QueryCallBack'
import { DbUpgradeListener } from './db/DbUpgradeListener'
import { Selector } from './db/sqlite/Selector'
import { GlobalContext } from './GlobalContext'
import { Context } from '@ohos.abilityAccessCtrl'


export class DbUtils {
    private daoMap: Map<string, any>;
    private daoConfig: DaoConfig;
    private debug = false;
    private allowTransaction = false;
    private queryCallBack: QueryCallBack
    private global:GlobalContext

    constructor(daoConfig: DaoConfig)

    constructor(dbDir: string, dbName: string, tableName: string,
                dbVersion: number, dbUpgradeListener: DbUpgradeListener)

    constructor(dbConfigOrDir?, dbName?, tableName?, dbVersion?, dbUpgradeListener?) {
        if (!!dbName) {
            this.daoConfig = new DaoConfig();
            this.daoConfig.setDbDir(dbConfigOrDir);
            this.daoConfig.setDbName(dbName);
            this.daoConfig.setTableName(tableName);
            this.daoConfig.setDbVersion(dbVersion);
            this.daoConfig.setDbUpgradeListener(dbUpgradeListener);
        } else {
            this.daoConfig = dbConfigOrDir;
        }
        this.global= GlobalContext.getContext()

        this.createStore();
    }

    public setCallback(callBack: QueryCallBack) {
        this.queryCallBack = callBack
    }

    public configDebug(debug: boolean): DbUtils {
        this.debug = debug;
        return this;
    }

    public createStore() {
        this.execNonQuery('', null, DaoType.CREATE_STORE);
    }

    public createTableIfNotExist() {
        var sqlInfo = SqlInfoBuilder.buildCreateTableSqlInfo(this);
        this.execNonQuery(sqlInfo, null, DaoType.CREATE_TABLE);
    }

    public save(valuesBucket: Map<string, any>) {
        this.execNonQuery(valuesBucket, null, DaoType.INSERT);
    }

    public saveAll(valueBuckets) {
        for (let i = 0;i < valueBuckets.length; i++) {
            this.execNonQuery(valueBuckets[i], null, DaoType.INSERT);
        }
    }

    public findAll(selector: Selector) {
        return this.execNonQuery("", selector, DaoType.QUERY)
    }

    public findFirst(selector: Selector) {
        this.execNonQuery("", selector, DaoType.QUERY_FIRST)
    }

    public update(valuesBucket: ESObject, selector: Selector) {
        this.execNonQuery(valuesBucket, selector, DaoType.UPDATE)
    }

    public delete(selector: Selector) {
        this.execNonQuery("", selector, DaoType.DELETE)
    }

    /**
     * 删除表
     * @param tableName
     */
    public dropTable(tableName: string) {
        if (this.daoConfig == null || this.daoConfig.getTableName() == null)return
        this.execNonQuery("DROP TABLE " + tableName, null, DaoType.DROPTABLE);
    }

    /**
     * 删除数据库
     */
    public dropDb() {
        let that = this
        if (this.daoConfig.getDbName() == null) return
        dataRdb.deleteRdbStore(this.global.getValue('context') as Context, this.daoConfig.getDbName())
            .then(() => {
                this.daoConfig.setDbName(null)
                that.queryCallBack.onSuccessDeleteDb()
            })
    }

    public execNonQuery<T>(sql: ESObject, selector: Selector, type: string): Array<T> {
        let that = this
        if (this.daoConfig.getDbName() == null) {
            that.queryCallBack.onFailQuery('数据库不存在')
            return
        }
        let tableName = this.daoConfig.getTableName()
        dataRdb.getRdbStore(this.global.getValue('context') as Context,
            {
                name: this.daoConfig.getDbName(),
                securityLevel:dataRdb.SecurityLevel.S1
            }, function (err, rdbStore) {
                if (err) {
                    console.info("Get RdbStore failed, err: " + err)
                    return
                }
                switch (type) {
                    case DaoType.CREATE_STORE:
                        that.queryCallBack.onSuccessCreateDb()
                        break;
                    case DaoType.CREATE_TABLE:
                        rdbStore.executeSql(sql, null, function () {
                            that.queryCallBack.onSuccessCreateTable()
                        })
                        break;
                    case DaoType.INSERT:
                        let promise = rdbStore.insert(tableName, sql)
                        promise.then(async (ret) => {
                            await
                            that.queryCallBack.onSuccessInsert(ret)
                        }).catch((err) => {
                            that.queryCallBack.onErrorInsert(err)
                        })
                        break;
                    case DaoType.QUERY:
                        rdbStore.query(selector == null ? new dataRdb.RdbPredicates(tableName) : selector.predicates, selector.getQueryColumns())
                            .then((resultSet) => {
                                if (resultSet.rowCount < 0) return
                                that.queryCallBack.onSuccessQuery(resultSet)
                                resultSet.close()
                                resultSet = null
                            })
                        break;
                    case DaoType.QUERY_FIRST:
                        rdbStore.query(selector.predicates, selector.getQueryColumns())
                            .then((resultSet) => {
                                if (resultSet.rowCount < 0) return
                                var
                                resultMap = new Map()
                                for (var i = 0; i < 1; i++) {
                                    resultSet.goToNextRow()
                                    for (var j = 0; j < selector.getQueryColumns()
                                        .length; j++) {
                                        const name = resultSet.getString(resultSet.getColumnIndex(selector.getQueryColumns()[j]))
                                        resultMap.set(selector.getQueryColumns()[j], name)
                                    }
                                }
                                that.queryCallBack.onSuccessQueryFirst(resultMap)
                                resultSet.close()
                                resultSet = null
                            })
                        break;
                    case DaoType.UPDATE:
                        rdbStore.update(sql, selector.predicates)
                            .then(async (ret) => {
                                await
                                that.queryCallBack.onSuccessUpdate(ret)
                            }).catch((err) => {
                            that.queryCallBack.onErrorUpdate(err)
                        })
                        break
                    case DaoType.DELETE:
                        rdbStore.delete(selector.predicates)
                            .then((rows) => {
                                that.queryCallBack.onSuccessDelete(rows)
                            }).catch((err) => {
                            that.queryCallBack.onErrorDelete(err)
                        })
                        break
                    case DaoType.DROPTABLE:
                        rdbStore.executeSql(sql, null, function () {
                            if (err) {
                                return
                            }
                            that.queryCallBack.onSuccessDropTable()
                        })
                        break
                }
            })
    }

    public getDaoConfig(): DaoConfig {
        return this.daoConfig;
    }
}